/* Orbas Naming Server:
 *     A open source CORBA CosNaming implementation from Huihoo.
 *
 * Copyright 2003 Huihoo Power, Inc. All Right Reserved.
 *
 * This software is licensed under LGPL license.
 * See terms of license at gnu.org.
 *
 * For more information, visit:
 *
 * http://www.huihoo.org/orbas
 */
package org.huihoo.orbas.naming.util;

/**
 * This source code from jacorb.
 * @author Gerald Brose
 * @version $Id: CorbaLoc.java,v 1.2 2003/05/07 08:22:59 cocia Exp $
 */

import java.util.StringTokenizer;

public class CorbaLoc {
	private String keyString;
	private byte[] key;
	private String bodyString;

	public static class ObjectAddress {
		/** protocol type information */
		public String protocol_identifier = "iiop";

		/** protocol version information */
		public int major = 1;
		public int minor = 0;

		/** IP address information */
		public String host = "127.0.0.1";
		public int port = 2809; // corbaloc default port number

		public org.omg.IIOP.Version getVersion() {
			return new org.omg.IIOP.Version((byte) major, (byte) minor);
		}
	}

	public ObjectAddress[] objectAddressList;

	public CorbaLoc(String addr) {
		parse(addr);
	}

	/**
	 * parses a string representing a corbaloc: reference
	 */

	private void parse(String addr) {
		if (addr == null || !addr.startsWith("corbaloc:"))
			throw new IllegalArgumentException(
					"URL must start with \'corbaloc:\'");

		String sb;
		if (addr.indexOf('/') == -1) {
			sb = new String(addr.substring(addr.indexOf(':') + 1));
			if (addr.startsWith("corbaloc:rir:")) {
				// default key string for rir protocol
				keyString = "NameService";
			} else {
				keyString = null;
			}
			key = new byte[0];
		} else {
			sb = new String(addr.substring(addr.indexOf(':') + 1,
					addr.indexOf('/')));
			keyString = addr.substring(addr.indexOf('/') + 1);
			key = parseKey(keyString);
		}

		if (sb.indexOf(',') > 0) {
			StringTokenizer tokenizer = new StringTokenizer(sb, ",");
			objectAddressList = new ObjectAddress[tokenizer.countTokens()];

			for (int i = 0; i < objectAddressList.length; i++)
				objectAddressList[i] = parseObjectAddress(tokenizer.nextToken());
		} else
			objectAddressList = new ObjectAddress[] { parseObjectAddress(sb) };

		bodyString = sb;
	}

	private ObjectAddress parseObjectAddress(String addr) {
		if (addr.indexOf(':') == -1)
			throw new IllegalArgumentException(
					"Illegal object address format: " + addr);

		ObjectAddress result = new ObjectAddress();

		if (addr.equals("rir:")) {
			/* resolve initials references protocol */
			result.protocol_identifier = "rir";
			return result;
		} else if (addr.indexOf(':') == 0 || addr.startsWith("iiop:")
				|| addr.startsWith("ssliop:")) {
			if (addr.indexOf(':') != 0) {
				// protocol exclicitely specified
				result.protocol_identifier = addr.substring(0,
						addr.indexOf(':'));
			}
			// else: use default "iiop"

			String version_and_host = addr.substring(addr.indexOf(':') + 1);
			if (version_and_host.length() == 0)
				throw new IllegalArgumentException(
						"Illegal IIOP protocol format in object address format: "
								+ addr);

			/* IIOP */
			String host;
			if (version_and_host.indexOf('@') != -1) {
				String version = version_and_host.substring(0,
						version_and_host.indexOf('@'));
				if (version.indexOf('.') != -1) {
					try {
						result.major = Integer.parseInt(version.substring(0,
								version.indexOf('.')));
						result.minor = Integer.parseInt(version
								.substring(version.indexOf('.') + 1));
					} catch (NumberFormatException nfe) {
						throw new IllegalArgumentException(
								"Illegal version format for IIOP protocol in object address format: "
										+ addr);
					}
				}
				host = version_and_host
						.substring(version_and_host.indexOf('@') + 1);
			} else
				host = version_and_host;
			if (host.indexOf(':') != -1) {
				try {
					result.host = host.substring(0, host.indexOf(':'));
					result.port = Integer.parseInt(host.substring(host
							.indexOf(':') + 1));
				} catch (NumberFormatException ill) {
					throw new IllegalArgumentException(
							"Illegal port number in IIOP object address format: "
									+ addr);
				}
			} else
				result.host = host;
			return result;
		} else
			throw new IllegalArgumentException(
					"Illegal protocol in object address format: " + addr);
	}

	private static boolean legalChar(char c) {
		if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')
				|| (c >= 'A' && c <= 'Z'))
			return true;
		else
			return (c == ';' || c == '/' || c == ':' || c == '?' || c == '@'
					|| c == '&' || c == '=' || c == '+' || c == '$' || c == ','
					|| c == '_' || c == '.' || c == '!' || c == '~' || c == '*'
					|| c == '\'' || c == '-' || c == '(' || c == ')');
	}

	private static byte hexValue(char c) {
		return (byte) ((c >= 'a') ? (10 + c - 'a')
				: ((c >= 'A') ? (10 + c - 'A') : (c - '0')));
	}

	private static char hexDigit(byte b) {
		if ((b & 0xf0) != 0)
			throw new IllegalArgumentException("Hex digit out of range " + b);

		return (char) (b < 10 ? '0' + (char) b : 'A' + (char) b - 10);
	}

	private static boolean isHex(char c) {
		return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
	}

	public static byte[] parseKey(String s) {
		char[] tmp = s.toCharArray();
		int count = tmp.length;

		for (int i = 0; i < tmp.length; i++) {
			if (!legalChar(tmp[i])) {
				if (tmp[i] == '%') {
					if (isHex(tmp[i + 1]) && isHex(tmp[i + 2])) {
						count -= 2;
						i += 2;
					} else
						throw new IllegalArgumentException(
								"Illegal escape in URL character");
				} else
					throw new IllegalArgumentException(
							"URL character out of range: " + tmp[i]);
			}
		}

		byte[] result = new byte[count];
		int idx = 0;

		for (int i = 0; i < count; i++) {
			if (legalChar(tmp[idx]))
				result[i] = (byte) tmp[idx++];
			else {
				result[i] = (byte) ((hexValue(tmp[idx + 1])) << 4 | hexValue(tmp[idx + 2]));
				idx += 3;
			}
		}
		return result;
	}

	public static String parseKey(byte[] key) {
		StringBuffer sb = new StringBuffer();

		for (int i = 0; i < key.length; i++) {
			if (!legalChar((char) key[i])) {
				sb.append('%');
				sb.append(hexDigit((byte) (key[i] >> 4)));
				sb.append(hexDigit((byte) (key[i] & 0x0f)));
			} else {
				sb.append((char) key[i]);
			}
		}
		return sb.toString();
	}

	public String toString() {
		return "corbaloc:" + body();
	}

	public String body() {
		StringBuffer sb = new StringBuffer();

		sb.append(bodyString);

		if (keyString != null)
			sb.append("/" + keyString);

		return sb.toString();
	}

	public String getKeyString() {
		return keyString;
	}

	public byte[] getKey() {
		return key;
	}

	public void defaultKeyString(String s) {
		if (keyString == null)
			keyString = s;
		else
			throw new RuntimeException(
					"KeyString not empty, cannot default to " + s);
	}

	public String toCorbaName(String str_name) {
		if (getKeyString() == null)
			defaultKeyString("NameService");

		if (str_name != null && str_name.length() > 0) {
			try {
				return "corbaname:" + body() + "#" + str_name;
			} catch (Exception e) {
				return null;
			}
		} else
			return "corbaname:" + body();

	}

	public static void main(String[] args) {
		for (int i = 0; i < args.length; i++) {
			System.out.println(new CorbaLoc(args[i]).toString());
		}

	}

}
